package dk.javacode.srsm.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.apache.log4j.Logger;

import dk.javacode.srsm.exceptions.MappingException;

public class ReflectionUtil {
	
	private static Logger log = Logger.getLogger(ReflectionUtil.class);
	/**
	 * Set the value of the fieldname on the object given.
	 * 
	 * @param object
	 *            The object to set the value on.
	 * @param fieldname
	 *            The field name to set the value on.
	 * @param value
	 *            The value to set.
	 * @throws MappingException 
	 */
	public static <E> void setFieldValue(E object, Field field, Object value) throws MappingException {
			field.setAccessible(true);
			try {
				field.set(object, value);
			} catch (IllegalArgumentException e) {
				throw new MappingException("Unable to set value on field: " + field.getName() + " (field.type=" + field.getType() + ", value.type=" + value.getClass() + ")", e);
			} catch (IllegalAccessException e) {
				throw new MappingException("Unable to set value on field: " + field.getName() + " (field not accessible)", e);
			}
	}

	public static Field getField(Class<?> clazz, String name) throws MappingException {
		Field[] fields = clazz.getDeclaredFields();
		for (Field f : fields) {
			if (name.equalsIgnoreCase(f.getName())) {
				return f;
			}
		}
		log.debug("No field with name: " + name);
		return null;
	}
	
	public static <E> void invokeMethod(E target, Method method, Object value) throws MappingException {
		try {
			method.invoke(target, new Object[] { value });
		} catch (IllegalArgumentException e) {
			throw new MappingException("Unable to invoke method (" + method.getName() + ") with arguments: " + value, e);
		} catch (IllegalAccessException e) {
			throw new MappingException("Method (" + method.getName() + ") not accessible.", e);
		} catch (InvocationTargetException e) {
			throw new MappingException("Error in method: " + method.getName(), e);
		}
	}
	
	public static Method getSetMethodForField(Class<?> clazz, Field field) throws MappingException {
		Method[] methods = clazz.getDeclaredMethods();
		for (Method m : methods) {
			if (m.getName().equalsIgnoreCase("set" + field.getName())) {
				return m;
			}
		}
		return null;
	}

	public static <E> E getNewInstance(Class<E> clazz) throws MappingException {
		try {
			Constructor<E> constructor = clazz.getConstructor(new Class<?>[0]);
			return constructor.newInstance(new Object[0]);
		} catch (Exception e) {
			throw new MappingException("Unable to initialize class: " + clazz.getName(), e);
		}
	}
}
